﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;

namespace Shared.Method
{
    public class HttpArgs
    {
        public string Url { get; set; }
        public string Host { get; set; }
        public string Accept { get; set; }
        public string Referer { get; set; }
        public string Cookie { get; set; }
        public string Body { get; set; }
        public int Port { get; set; }
    }

    public class HttpBaseClass
    {
        /// <summary>
        /// 提交方法
        /// </summary>
        public enum HttpMethod
        {
            GET,
            POST
        }

        #region Socket
        private YcdBitTcpClient _ycdBitTcp = null;
        private string _localIp = null;
        private int _localPort = 0;

        private string _serverIp = null;
        private int _serverPort = 0;

        public string LastError { get { lock (objLock) return _error; } }
        private string _error;

        private object objLock = new object();

        private Queue<string> _recvMsg = new Queue<string>();

        /// <summary>
        /// 初始化参数
        /// </summary>
        /// <param name="localIp">本地IP</param>
        /// <param name="localPort">本地端口</param>
        /// <param name="serverIp">服务器IP</param>
        /// <param name="serverPort">服务器端口</param>
        public HttpBaseClass(string localIp, int localPort, string serverIp, int serverPort)
        {
            _localIp = localIp;
            _localPort = localPort;
            _serverIp = serverIp;
            _serverPort = serverPort;
        }

        public bool Start()
        {
            try
            {
                if (string.IsNullOrEmpty(_localIp) || string.IsNullOrEmpty(_serverIp))
                {
                    lock (objLock) _error = "服务器或本地IP为空";
                    return false;
                }

                if (_localPort <= 1024)
                {
                    lock (objLock) _error = "本地端口不能小于1024";
                    return false;
                }

                _ycdBitTcp = new YcdBitTcpClient(_serverIp, _serverPort, _localIp, _localPort);

                _ycdBitTcp.OnConnected += _ycdBitTcp_OnConnected;
                _ycdBitTcp.OnError += _ycdBitTcp_OnError;
                _ycdBitTcp.OnReceive += _ycdBitTcp_OnReceive;

                _ycdBitTcp.Start();

                return true;
            }
            catch (Exception ex)
            {
                lock (objLock) _error = ex.Message;
                return false;
            }
        }

        /// <summary>
        /// 关闭HTTP客户端
        /// </summary>
        public void Close()
        {
            _ycdBitTcp?.Stop();
        }

        //接收信息
        private void _ycdBitTcp_OnReceive(byte[] byteData, int length)
        {
            //转换成UTF8格式存储
            if (length == 0) return;
            string msg = Encoding.UTF8.GetString(byteData, 0, length);
            _recvMsg.Enqueue(msg);

        }
        //错误显示
        private void _ycdBitTcp_OnError(string obj)
        {
            lock (objLock) _error = obj;
        }
        //连接信息
        private void _ycdBitTcp_OnConnected(string obj)
        {

        }


        public byte[] Get(IPEndPoint endpoint, HttpArgs args)
        {
            return InternalSocketHttp(endpoint, args, HttpMethod.GET);
        }

        public byte[] Post(IPEndPoint endpoint, HttpArgs args)
        {
            return InternalSocketHttp(endpoint, args, HttpMethod.POST);
        }

        private byte[] InternalSocketHttp(IPEndPoint endpoint, HttpArgs args, HttpMethod method)
        {
            using (Socket sK = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
            {
                try
                {
                    sK.Connect(endpoint);
                    if (sK.Connected)
                    {
                        byte[] buff = ParseHttpArgs(method, args);
                        if (sK.Send(buff) > 0)
                        {
                            return ParseResponse(endpoint, sK, args);
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
            return null;
        }

        private byte[] ParseResponse(IPEndPoint endpoint, Socket sK, HttpArgs args)
        {
            //尝试10秒时间读取协议头
            CancellationTokenSource source = new CancellationTokenSource();

            Task<string> myTask = Task.Factory.StartNew(new Func<object, string>(ReadHeaderProcess), sK, source.Token);

            myTask.Wait();

            string header = myTask.Result;
            if (header.StartsWith("HTTP/1.1 302"))
            {
                int start = header.ToUpper().IndexOf("LOCATION");
                if (start > 0)
                {
                    string temp = header.Substring(start, header.Length - start);
                    string[] sArry = Regex.Split(temp, "\r\n");
                    args.Url = sArry[0].Remove(0, 10);
                    return Get(endpoint, args);  //注意：302协议需要重定向
                }
            }
            else if (header.StartsWith("HTTP/1.1 200"))  //继续读取内容
            {
                int start = header.ToUpper().IndexOf("CONTENT-LENGTH");
                int content_length = 0;
                if (start > 0)
                {
                    string temp = header.Substring(start, header.Length - start);
                    string[] sArry = Regex.Split(temp, "\r\n");
                    content_length = Convert.ToInt32(sArry[0].Split(':')[1]);
                    if (content_length > 0)
                    {
                        byte[] bytes = new byte[content_length];
                        if (sK.Receive(bytes) > 0)
                        {
                            return bytes;
                        }
                    }
                }
                else
                {
                    return ParseResponse(sK);
                }
            }
            else
            {
                return Encoding.Default.GetBytes(header);
            }

            return null;
        }

        /// <summary>
        ///  读取协议头
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
        private string ReadHeaderProcess(object args)
        {
            Socket sK = (Socket)args;
            StringBuilder bulider = new StringBuilder();
            while (true)
            {
                byte[] buff = new byte[65535];
                int read = sK.Receive(buff, SocketFlags.None);
                if (read > 0)
                {
                    bulider.Append(Encoding.UTF8.GetString(buff, 0, read));
                }
                string temp = bulider.ToString();
                if (temp.Contains("\r\n\r\n"))
                {
                    break;
                }
            }
            return bulider.ToString();
        }

        /// <summary>
        /// 注意：此函数可能产生死循环
        /// </summary>
        /// <param name="ssl"></param>
        /// <returns></returns>
        static byte[] ParseResponse(Socket sK)
        {
            ArrayList array = new ArrayList();
            StringBuilder bulider = new StringBuilder();
            int length = 0;
            while (true)
            {
                byte[] buff = new byte[1024];
                int len = sK.Receive(buff);
                if (len > 0)
                {
                    length += len;
                    byte[] reads = new byte[len];
                    Array.Copy(buff, 0, reads, 0, len);
                    array.Add(reads);
                    bulider.Append(Encoding.Default.GetString(reads));
                }
                string temp = bulider.ToString();
                if (temp.ToUpper().Contains("</HTML>"))
                {
                    break;
                }
            }
            byte[] bytes = new byte[length];
            int index = 0;
            for (int i = 0; i < array.Count; i++)
            {
                byte[] temp = (byte[])array[i];
                Array.Copy(temp, 0, bytes,
                    index, temp.Length);
                index += temp.Length;
            }
            return bytes;
        }

        #endregion

        #region  Helper自写

        private static byte[] ParseHttpArgs(HttpMethod method, HttpArgs args)
        {
            StringBuilder builder = new StringBuilder();

            if (method.Equals(HttpMethod.POST))
            {
                builder.AppendLine(string.Format("POST {0} HTTP/1.1", args.Url));
                builder.AppendLine("Content-Type: application/x-www-form-urlencoded");
                builder.AppendLine(string.Format("Content-Length: {0}", Encoding.Default.GetBytes(args.Body).Length));
            }
            else
            {
                builder.AppendLine(string.Format("GET {0} HTTP/1.1", args.Url));
            }

            builder.AppendLine(string.Format("Host: {0}",args.Host));
            builder.AppendLine("Connection: keep-alive");
            builder.AppendLine("Cache-Control: max-age=0");
            builder.AppendLine("Upgrade-Insecure-Requests: 1");
            builder.AppendLine("User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36 Maxthon/5.2.5.4000");
            builder.AppendLine("Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8");
            builder.AppendLine("DNT: 1");
            builder.AppendLine("Accept-Encoding: gzip, deflate");
            builder.AppendLine("Accept-Language: zh-CN\r\n");

            if (method.Equals(HttpMethod.POST))
            {
                builder.Append(args.Body);
            }

            string httpMessage = builder.ToString();

            return Encoding.Default.GetBytes(httpMessage);
        }


        /// <summary>
        /// 获取网页信息
        /// </summary>
        /// <param name="args">参数</param>
        /// <param name="keyStr">完成标志</param>
        /// <returns></returns>
        public string GetHttpMsg(HttpArgs args, string keyStr)
        {
            try
            {
                byte[] sendData = ParseHttpArgs(HttpMethod.GET, args);

                _ycdBitTcp?.SendData(sendData, sendData.Length);

                string httpMsg = "";
                //读取数据
                for (int i = 0; i < 5; i++)
                {
                    Thread.Sleep(400);
                    string msg = null;

                    while (_recvMsg.Count > 0 && (msg = _recvMsg.Dequeue()) != null)
                    {
                        httpMsg += msg;
                    }

                    if (httpMsg.Contains(keyStr))
                    {
                        break;
                    }
                }

                //清除头文件
                if (string.IsNullOrEmpty(httpMsg) == false && httpMsg.Contains("---"))
                {
                    int index = httpMsg.IndexOf("---");
                    //Console.WriteLine("出现分隔符的起始位置是：" + index);

                    httpMsg = httpMsg.Substring(index);
                }

                return httpMsg;
            }
            catch (Exception ex)
            {
                return ex.Message;
            }
        }

        private static readonly int PACKSIZE = 0x20000;
        /// <summary>
        /// 直接获取网页的静态方法
        /// </summary>
        /// <param name="localIp">本地IP</param>
        /// <param name="localPort">本地端口</param>
        /// <param name="serverIp">目的IP</param>
        /// <param name="serverPort">目的端口</param>
        /// <param name="args">HTTP参数</param>
        /// <param name="keyStr">关键字</param>
        /// <param name="httpMsg">获取的网页信息</param>
        /// <returns></returns>
        public static bool GetHttpMsg(string localIp, int localPort,
            string serverIp, int serverPort,
            HttpArgs args, string keyStr,
            out string httpMsg, HttpMethod method = HttpMethod.GET)
        {
            httpMsg = string.Empty;

            try
            {
                if (string.IsNullOrEmpty(localIp) || string.IsNullOrEmpty(serverIp))
                {
                    httpMsg = "传入的参数错误，本地IP或服务IP为空";
                    return false;
                }

                if (localPort <= 1024)
                {
                    httpMsg = "本地端口不能小于1024";
                    return false;
                }

                IPEndPoint localEP = new IPEndPoint(IPAddress.Parse(localIp), localPort);
                var client = new TcpClient(localEP);
                client.Connect(serverIp, serverPort);

                //获取需要发送的内容
                byte[] sendData = ParseHttpArgs(method, args);

                using (var stream = client.GetStream())
                {
                    stream.Write(sendData, 0, sendData.Length);

                    var recv = new byte[PACKSIZE];

                    bool hasKey = false;
                    //读取数据
                    for (int i = 0; i < 10; i++)
                    {
                        Thread.Sleep(300);
                        //string msg = null;

                        int length = stream.Read(recv, 0, PACKSIZE);

                        if (length == 0)
                        {
                            client.Close();
                            throw new Exception("获取数据长度为0");
                        }

                        httpMsg += Encoding.Default.GetString(recv.Take(length).ToArray());

                        if (httpMsg.Contains(keyStr))
                        {
                            hasKey = true;
                            break;
                        }
                    }

                    //清除头文件
                    if (string.IsNullOrEmpty(httpMsg) == false && httpMsg.Contains("---"))
                    {
                        int index = httpMsg.IndexOf("---");
                        //Console.WriteLine("出现分隔符的起始位置是：" + index);

                        httpMsg = httpMsg.Substring(index);
                    }
                    
                    client.Close();
                    return hasKey;
                }
            }
            catch (Exception ex)
            {
                httpMsg += ex.Message;
                return false;
            }
        }

        #endregion
    }
}

